#   Docker. Часть 2

1.  [Введение в сети](#введение-в-сети)
2.  [Работа с хранилищем](#работа-с-хранилищем)
3.  [YAML, docker-compose](#yaml-docker-compose)
3.  [Задание на лабораторную работу](#задание-на-лабораторную-работу)

---

##  Введение в сети

### Обзор сетевых возможностей Docker

---

1.  __Bridge Network__

    *   Описание
        *   Мостовая сеть представляет собой встроенную сеть Docker,
            которая позволяет контейнерам общаться между собой  и  с
            хостовой системой

    *   Применение
        *   Подходит для локальных сред разработки, где контейнеры
            должны иметь связь друг с другом, но не обязательно  с
            внешним миром.

    *   Характеристики
        1.  Каждый контейнер, запущенный в bridge-сети, получает
            уникальный IP-адрес внутри этой сети

        2.  Контейнеры могут общаться друг с другом по их IP-адресам

        3.  Docker создает NAT (Network Address Translation),  чтобы
            контейнеры имели доступ к внешней сети.

2.  __Host Network__

    *   Описание
        *   Контейнеры, запущенные в режиме сети хоста, используют  сетевое
            пространство хоста, а не свои собственные изолированные сетевые
            пространства

    *   Применение
        *   Подходит, когда контейнерам необходим полный доступ к сетевым
            ресурсам хоста, и изоляция не требуется
    
    *   Характеристики
        1.  Контейнеры видят сеть хоста напрямую и могут
            использовать ее      IP-адреса   и     порты

        2.  Это может повысить производительность, поскольку
            нет дополнительного уровня NAT  и  маршрутизации

3.  __Overlay Network__

    *   Описание
        *   Сеть оверлея позволяет контейнерам, размещенным на разных
            хостах, общаться между собой, как если бы они  находились
            на одном хосте

    *   Применение
        *   Используется в средах с контейнерами, развернутыми на нескольких
            узлах (например, в кластере Docker  Swarm или  Kubernetes),  где
            необходимо обеспечить связь между   контейнерами, работающими на
            разных узлах

    *   Характеристики
        1.  Поддерживает многоконтейнерные приложения, работающие в
            распределенной среде

        2.  Docker     автоматически    управляет  маршрутизацией и
            шифрованием данных между хостами.

4.  __Macvlan Network__

    *   Описание
        *   Macvlan-сеть предоставляет каждому контейнеру   свой
            уникальный MAC-адрес и позволяет им быть частью сети,
            как и физическое устройство в сети

    *   Применение
        *   Используется, когда требуется прозрачный доступ к сети,
            и контейнерам нужно быть частью физической сети

    *   Характеристики
        1.  Каждый контейнер получает  свой
            IP-адрес внутри физической сети

        2.  Docker     автоматически    управляет  маршрутизацией и
            шифрованием данных между хостами.

---

### Применение каждого типа сети в контексте Docker-контейнеров

*   Этот раздел стоит проходить так,  копируете Dockerfile (он
    очень       простой,      мы      просто         запускаем
    [nginx](https://en.wikipedia.org/wiki/Nginx)),    читаете
    описание и выполняете bash скрипт,  с пониманием того, что
    вы создаете. Это __не__ практическая  часть   лабораторной
    работы, это просто интерактивная работа с рассматриваемыми
    темами

1.  __Bridge Network__

    *   Dockerfile

        ```dockerfile

        # Используем базовый образ с веб-сервером
        FROM nginx:latest

        # Открываем порт 80
        EXPOSE 80

        # Копируем кастомный конфиг
        COPY nginx.conf /etc/nginx/nginx.conf

        ```

    *   Описание

        *   Сеть типа "Мост" создается по  умолчанию при установке  Docker

        *   Каждый контейнер, подключенный к  сети типа  "Мост",  получает
            свой уникальный IP-адрес в пределах виртуального моста.

        *   Сеть типа "Мост" обеспечивает изоляцию контейнеров, но они все
            могут связываться друг с другом

    *   bash

        ```bash

        # Создание Docker-сети типа "Мост"
        docker network create my_bridge_network

        # Запуск контейнера с использованием созданной сети
        docker run -d --name my_container --network my_bridge_network my_nginx_image

        ```

    *   Сценарий применения
        *   Создание изолированной локальной сети для взаимодействия контейнеров

    *   Пояснение
        *   Этот пример создает сеть типа "Мост" и запускает контейнер в этой сети.
            Поскольку сеть "Мост" предоставляет каждому контейнеру свой   IP-адрес,
            они могут взаимодействовать друг с другом внутри этой   сети.      Файл
            nginx.conf может содержать кастомные настройки веб-сервера.

2.  __Host Network__

    *   Dockerfile

        ```dockerfile

        # Используем базовый образ с веб-сервером Nginx
        FROM nginx:latest

        # Выставляем порт, который контейнер будет использовать
        EXPOSE 80

        ```

    *   Описание

        *   Контейнер использует сетевой стек хоста, а не свой собственный.
            Это означает, что контейнер может использовать сетевые  службы
            хоста напрямую

        *   Все порты, которые контейнер экспонирует, будут доступны
            напрямую на хосте

    *   bash

        ```bash
        
        docker run -d --name my_container --network host my_nginx_image

        ```

    *   Сценарий применения
        *   Контейнеры, работающие в сети хоста с полным доступом к сетевым ресурсам

    *   Пояснение
        *   Этот пример запускает контейнер, используя сеть типа "Хост", что означает,
            что контейнер использует сетевой стек хоста. Контейнер имеет полный доступ
            к сетевым службам хоста, и порты, открытые в контейнере, сразу же доступны
            снаружи

3.  __Overlay Network__

    *   Dockerfile

        ```dockerfile

        # Используем базовый образ с веб-сервером Nginx
        FROM nginx:latest

        # Выставляем порт, который контейнер будет использовать
        EXPOSE 80

        ```

    *   Описание

        *   Сеть типа "Overlay" позволяет объединять контейнеры, запущенные
            на разных хостах, в одну виртуальную сеть

        *   Часто используется в кластерных средах, таких как  Docker Swarm
            или Kubernetes

    *   bash

        ```bash
        
        # Инициализация Docker Swarm (если необходимо)
        docker swarm init

        # Создание Docker-сети типа "Overlay"
        docker network create -d overlay my_overlay_network

        # Запуск сервиса с использованием созданной сети
        docker service create --name my_service --network my_overlay_network my_nginx_image

        ```

    *   Сценарий применения
        *   Связь контейнеров, запущенных на разных узлах Docker Swarm

    *   Пояснение
        *   В этом примере используется сеть типа "Overlay", которая позволяет
            связывать контейнеры на разных узлах Docker Swarm. После  создания
            сети, сервис (контейнер) запускается с использованием  этой  сети,
            обеспечивая связь между контейнерами, даже если  они  распределены
            по разным узлам

4.  __Macvlan Network__

    *   Dockerfile

        ```dockerfile

        # Используем базовый образ с веб-сервером Nginx
        FROM nginx:latest

        # Выставляем порт, который контейнер будет использовать
        EXPOSE 80

        ```

    *   Описание
        *   Macvlan-сеть предоставляет каждому контейнеру   свой
            уникальный MAC-адрес и позволяет им быть частью сети,
            как и физическое устройство в сети

    *   bash

        ```bash
        
        # Создание Docker-сети типа "Macvlan"
        docker network create -d macvlan --subnet=192.168.1.0/24 --gateway=192.168.1.1 -o parent=eth0 my_macvlan_network

        # Запуск контейнера с использованием созданной сети
        docker run -d --name my_container --network my_macvlan_network my_nginx_image

        ```

    *   Сценарий применения
        *   Контейнеры с прозрачным доступом к сетевой инфраструктуре

    *   Пояснение
        *   Сеть типа "Macvlan" позволяет     контейнерам   использовать  физические
            MAC-адреса и присваивать им IP-адреса из внешней сети. В данном  примере создается сеть "Macvlan" с использованием определенного   интерфейса  (в
            данном случае, eth0), и контейнер запускается с использованием этой сети

---

### Понимание изоляции сети в контейнерах

*   В контексте контейнеризации, изоляция сети является важным аспектом для
    обеспечения безопасности и эффективности работы контейнеров.   Изоляция
    сети в контейнерах обеспечивается несколькими механизмами:

    1.  __Пространство имен сети (Network Namespace)__: Каждый контейнер имеет свое
        собственное пространство имен сети, что означает,   что   контейнер   видит
        только свою собственную сетевую     конфигурацию. Это    включает  в   себя
        собственные сетевые интерфейсы,     IP-адреса, таблицы  маршрутизации     и
        правила файрвола

    2.  __Виртуальные сетевые устройства (veth pairs)__: При создании контейнера
        создается пара виртуальных   сетевых   устройств, одно     из    которых
        подключается к пространству имен     хоста, а другое к пространству имен
        контейнера. Это     обеспечивает коммуникацию между контейнером и хостом

    3.  __Мосты (Bridges)__: Мосты используются для связи между виртуальными сетевыми
        устройствами в разных пространствах имен. Обычно    создается  мост на хосте,
        и виртуальные сетевые устройства  контейнеров    подключаются к  этому  мосту

    4.  __Ограничение доступа к сетевым ресурсам__: Можно применять  правила   файрвола
        и другие настройки для ограничения   сетевого доступа  контейнеров.   Например,
        Docker позволяет определять правила   файрвола   для контейнера при его запуске

    5.  __Ограничение сетевого трафика__: Можно ограничивать пропускную способность
        сетевых интерфейсов     контейнеров,    чтобы   предотвратить    чрезмерное
        использование ресурсов.

---

##  Работа с хранилищем

### Введение в хранилища Docker

---

1.  Работа с Volume

*   __Создание и управление томами (Volumes)__

    *   Тома в Docker - это механизм для сохранения и управления данными,
        которые могут быть общими между контейнерами или    сохранены  на
        хост-системе. Вот как можно создать и управлять томами:

        ```bash

        # Эта команда создаст том с именем "my_volume"
        docker volume create my_volume

        ```

        ```bash

        # Эта команда выведет список всех созданных томов
        docker volume ls

        ```

        ```bash

        # Удаляет том с именем "my_volume". Обратите внимание, что
        # том должен быть не присоединен к какому-либо  контейнеру
        # перед удалением

        docker volume rm my_volume
        
        ```

*   __Применение томов для сохранения данных__

    *   Теперь давайте рассмотрим, как использовать тома для сохранения
        данных между контейнерами или сохранения их на хост-системе

    *   Связывание тома с контейнером

        ```bash

        # Эта команда связывает том "my_volume" с указанным  путем в
        # контейнере "my_image".
        # 
        # Теперь все данные, записанные в этот путь в    контейнере,
        # будут сохраняться в томе.

        docker run -v my_volume:/path/in/container my_image

        ```

    *   Связывание тома с хост-системой

        ```bash

        # Эта команда связывает указанный путь на хост-системе  с
        # путем в контейнере. Таким образом, данные, записанные в
        # контейнере в этот путь, будут сохранены на хост-системе

        docker run -v /host/path:/container/path my_image

        ```

    *   Использование тома для постоянного хранения данных

        ```bash

        # В этом примере том "my_volume" применяется   для    хранения
        # данных в папке "/data" в контейнере. Даже после   завершения
        # контейнера данные останутся в томе и могут быть использованы
        # в следующих контейнерах.

        docker run -v my_volume:/data my_image

        ```

2.  Работа с Bind Mount

*   __Что такое Bind Mount в Docker__

*   Bind Mount - это механизм в Docker, который позволяет  связывать   пути
    внутри контейнера с путями на хостовой файловой системе.  Это позволяет
    контейнерам и хостовой системе      взаимодействовать     и   совместно
    использовать файлы и директории.

*   Создание Bind Mount

    *   Для создания Bind Mount необходимо указать путь на хостовой системе и
        путь внутри контейнера в опциях запуска контейнера. Пример команды:

        ```bash

        # Эта команда создаст связь между указанным
        # путем на хосте и   путем    в  контейнере
        docker run -v /путь/на/хосте:/путь/в/контейнере имя_образа

        ```

*   Пример использования Bind Mount

    *   Рассмотрим пример использования Bind Mount для     сохранения данных
        при работе с веб-приложением в контейнере. Предположим, у   нас есть
        веб-сервер в контейнере, и мы хотим сохранить логи на       хостовой
        системе.

        ```bash

        # Теперь все логи, записанные в /путь/в/контейнере   внутри
        # контейнера, будут сохраняться на хостовой системе по пути
        # /путь/на/хосте
        docker run -v /путь/на/хосте:/путь/в/контейнере имя_образа_веб-сервера

        ```

*   Преимущества Bind Mount:

    *   Простота использования: Bind Mount легко настраивать и использовать.
        Нет необходимости в дополнительной  конфигурации     Docker Volumes.

    *   Доступность данных вне контейнера: Данные, хранящиеся в Bind Mount,
        могут быть легко доступны извне контейнера. Это особенно удобно для
        обмена данными между хостовой системой и контейнером

*   Ограничения Bind Mount:

    *   Зависимость от структуры хостовой системы: Bind   Mount   привязывается
        к конкретным путям на хостовой системе, что может создавать зависимости
        от структуры файловой системы хоста

    *   Потенциальные проблемы с безопасностью: Использование Bind Mount  может
        создать потенциальные проблемы с безопасностью, так как контейнер может
        получить доступ к файлам на хосте.

---

##  YAML, docker-compose

### Введение в YAML (Yet Another Markup Language)

*   YAML (Yet Another Markup Language, like [Yandex](https://yandex.ru/company/history/1993)) 
    представляет собой    формат    данных,
    разработанный для человекочитаемого представления структурированных данных.

*   Он широко используется в различных областях, включая конфигурационные файлы,
    передачу данных между приложениями и другие сценарии,  где   удобочитаемость
    данных играет важную роль.

1.  __Синтаксис YAML__

    *   Синтаксис YAML основан на использовании отступов для обозначения
        вложенности блоков данных. Важно поддерживать    консистентность
        отступов в файле YAML. Вот основные элементы синтаксиса:

        1.  **Отступы и вложенность**

            *   Отступы выполняют роль визуального разделителя блоков данных.
            *   Отступы можно делать пробелами или табуляцией,
                но не следует их смешивать.

            ```yaml
            
            key1:
                key2: value
                key3:
                    - item1
                    - item2
            
            ```

        2.  **Скалярные значения**

            *   Простые значения, такие как строки и числа, могут быть
                представлены        без          использования кавычек

            ```yaml
            
            name: John Doe
            age: 30
            
            ```

        3.  **Списки**

            *   Списки представляются при помощи дефиса и пробела

            ```yaml

            fruits:
                - apple
                - banana
                - orange

            ```

        4.  **Ассоциативные массивы**

            *   Пары ключ-значение используются  для
                представления ассоциативных массивов

            ```yaml

            person:
                name: Alice
                age: 25

            ```

        5.  **Кавычки**

            *   Кавычки используются для явного   указания
                строк или значений, содержащих специальные
                символы

            ```yaml

            message: "This is a string with special characters: !@#$%^&*"

            ```

        6.  **Многострочные строки**

            *   YAML поддерживает многострочные  строки
                для более удобного представления текста

            ```yaml

            description: |
                This is a multiline
                text in YAML.
    
            ```

---

2.  __Преимущества использования YAML для определения конфигураций__:

    1.  **Человекочитаемость**
        *   YAML разработан так, чтобы быть интуитивно понятным человеку.
            Его синтаксис приближен к естественному языку,    что  делает
            файлы YAML легкими для восприятия и редактирования

    2.  **Минимум символов**
        *   YAML минимизирует использование символов,       улучшая   читаемость.
            Нет необходимости в избыточных фигурных скобках или точках  с запятой

    3.  **Легкость в освоении**
        *   Синтаксис YAML прост и легок в освоении, что упрощает создание
            и редактирование конфигурационных файлов

    4.  **Иерархическая структура**
        *   YAML    поддерживает   создание   иерархических    структур   данных,  что
            особенно полезно для представления сложных конфигураций и структур  данных

    5.  **Поддержка комментариев**
        *   YAML позволяет добавлять комментарии, что упрощает документирование
            и пояснение конфигураций

    6.  **Использование в различных языках**
        *   YAML широко используется в различных языках программирования и
            инструментах для конфигураций, что делает его универсальным  и
            переносимым форматом данных.

---

3.  __Docker Compose__

    1.  **Что такое Docker Compose и зачем он нужен?**  

        *   Docker Compose - это инструмент для определения и  запуска
            многоконтейнерных приложений с использованием Docker.   Он
            позволяет определить всю инфраструктуру приложения в  виде
            кода в файле YAML, что облегчает создание и     управление
            контейнерами. Docker Compose предоставляет удобный  способ
            объединить несколько сервисов, контейнеров  и   настроек в
            одном файле, что упрощает   развертывание     и управление
            сложными приложениями.

        *   **Основные компоненты Docker Compose**

            1.  **docker-compose.yml**: Это основной файл конфигурации
                Docker Compose. В этом файле определяются все  сервисы,
                сети и объемы, необходимые для вашего приложения.

            2.  Сервисы (Services): Каждый сервис представляет отдельный
                контейнер, описывающий приложение или  часть  приложения.
                Например, веб-сервер, база данных, бэкенд и т.д.

            3.  Сети (Networks): Docker Compose     позволяет   определить
                пользовательские сети, которые используются     для  связи
                между контейнерами. Это обеспечивает изоляцию и безопасное
                взаимодействие между компонентами приложения.

            4.  Объемы (Volumes): Docker Compose позволяет   создавать и
                управлять Docker объемами,    которые  используются  для
                сохранения постоянных данных между запусками контейнеров.

        *   **Пример использования Docker Compose**

            ```yaml

            #   В этом примере мы определяем два сервиса: web и db

            #   Сервис web использует образ Nginx и проксирует
            #   порт 80 контейнера     на порт  8080     хоста

            #   Сервис db использует образ PostgreSQL и устанавливает некоторые
            #   переменные окружения  для создания  базы данных  и пользователя

            version: '3'

            services:
            web:
                image: nginx:latest
                ports:
                - "8080:80"

            db:
                image: postgres:latest
                environment:
                POSTGRES_DB: mydatabase
                POSTGRES_USER: myuser
                POSTGRES_PASSWORD: mypassword

            ```

        *   **Зачем Docker Compose нужен**

            1.  Упрощение развертывания: Docker Compose   позволяет   определить
                всю структуру приложения в одном файле, что делает развертывание
                проще и более управляемым.

            2.  Локализация конфигурации: Вся конфигурация, связанная с
                приложением, хранится в едином файле,   что    упрощает
                отслеживание и изменение параметров.

            3.  Многоконтейнерные приложения: При работе  с многоконтейнерными
                приложениями Docker Compose облегчает оркестрацию контейнеров,
                их взаимодействие и управление зависимостями.

            4.  Локальное тестирование: Docker Compose позволяет  создавать
                окружение для тестирования приложения локально, что полезно
                при разработке и отладке.

            5.  Поддержка в production-окружении: Docker Compose  также может
                использоваться для развертывания приложения в production,  но
                в более сложных сценариях часто применяют оркестраторы, такие
                как Docker Swarm или Kubernetes.

    2.  **Структура файла docker-compose.yml**

        *   Файл docker-compose.yml    представляет    собой конфигурационный
            файл для инструмента Docker Compose.    Этот    файл используется
            для определения и настройки многоконтейнерных приложений. Давайте
            подробно разберем структуру данного файла.

        *   Основные элементы структуры файла docker-compose.yml:

            1.  Версия (version): Указывает на версию синтаксиса Docker Compose.
                Версия определяет, какие функции  и  синтаксические  конструкции
                могут быть использованы в файле.

                ```yaml
                version: '3'
                ```

            2.  services (сервисы): Определяет набор    контейнеров, которые будут
                запущены в приложении. Каждый  сервис     внутри  блока   services
                представляет собой один контейнер. Для каждого сервиса указываются
                его настройки и зависимости.

                ```yaml
                services:
                    web:
                        image: nginx
                    database:
                        image: mysql
                ```

            3.  networks (сети): Позволяет определять пользовательские сети и
                их настройки для ваших сервисов.

                ```yaml
                networks:
                    frontend:
                    backend:
                ```

            4.  volumes (тома): Позволяет определять и настраивать Docker тома
                для ваших сервисов.

                ```yaml
                volumes:
                    data:
                ```

            5.  env_file (файлы окружения): Указывает на  файл(ы)    окружения,
                из которого будут считываться переменные окружения для сервиса.

                ```yaml
                services:
                    web:
                        env_file:
                        - .env
                ```

            6.  environment (переменные окружения): Позволяет устанавливать
                переменные окружения для сервиса.

                ```yaml
                services:
                    web:
                        environment:
                            - DEBUG=true
                            - DB_HOST=database
                ```

            7.  ports (порты): Определяет порты, которые могут быть использованы
                внутри контейнера и маппятся на порты хоста.

                ```yaml
                services:
                    web:
                        ports:
                        - "8080:80"
                ```

            8.  depends_on (зависимости): Указывает на другие сервисы, от которых
                зависит текущий сервис. Docker Compose будет обеспечивать  запуск
                зависимых сервисов перед текущим.

                ```yaml
                services:
                    web:
                        depends_on:
                            - database
                ```

            9.  restart (перезапуск): Устанавливает политику перезапуска для сервиса, 
                например, "always", "unless-stopped", и т.д.

                ```yaml
                services:
                    web:
                        restart: always
                ```

---

##  Задание на лабораторную работу

*   В корне этого приложения лежит файл, main.go,
    который представляет собой простой веб-сервер

    1.  Необходимо      запаковать       его      в       Docker-контейнер,
        для     этого       надо   написать      Dockerfile,     аналогично
        первой работе (в этом случае никаких переменных окружения указывать
        в Dockerfile __не__ надо, просто минимальный Dockerfile для запуска
        веб-сервера на Golang)

    2.  Написать   docker-compose.yml      для  запуска  веб-сервера,
        и сервера базы данных MySQL (оба сервиса     должны    быть в
        одном файле docker-compose), веб-сервер   и   сервер    MySQL
        должны быть в одной docker сети. Доказательством корректности
        выполнения данного шага является вызов веб-сервера  по адресу
        localhost:555   с   хостовой   машины   и   получения  ответа
        в виде "MySQL database connection has been successfully created"
        
        *   Для корректного   определения     параметров  запуска  контейнера,
            особое внимание необходимо обратить на имена переменных окружения,
            которые указаны в main.go

        *   Для создания контейнера под веб-сервер из main.go, необходимо учесть,
            что нужно сделать билд проекта, добавив в Dockerfile
            ```dockerfile
            RUN go mod init server
            RUN go get github.com/go-sql-driver/mysql
            RUN go build -o main .
            ```

        *   Важно понимать, что для  подключения  к  базе  данных,  которая  лежит
            в одной сети с web-server'ом вам не стоит использовать localhost:3306,
            вместо этого необходимо использовать  <название-контейнера-БД>:<порт>,
            которые вы определяете в docker-compose.yml. Почему?

        *   В docker-compose.yaml, для веб-сервера используйте depends_on, но,
            если попробовать сделать запрос сразу после запуска   контейнеров,
            подключение к БД не будет создано, хоть и depends_on    определяет 
            порядок запуска контейнеров. Почему?

    3.  В корне этого проекта лежит папка data,   необходимо    подключить    ее
        к базе данных MySQL и c хостовой операционной системы вызвать веб-сервер
        по адресу: localhost:555/programmers, если все выполнено   корректно  вы
        должны в консоли получить топ 10 самых  известных    программистов  всех
        времен по мнению портала reddit
        
        *   Чтобы это сделать надо переопределить место хранения директории,
            в которой хранятся файлы базы данных MySQL:
            ./<входные данные для базы данных>:/<путь, по которому они должны храниться в контейнере>, например, ./???:/var/lib/mysql

    *   Подсказка 1
        *   Если вы хотите пересобрать контейнер, не используя предыдущие его
            версии, добавьте флаг --build, например 
            ```bash
            docker-compose up --build
            ```
            
*   Ответы на эти вопросы в main_solution.md